#pragma once

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

#define THREADQUEUE_FOREACH_ERR(X)             \
    X(0, SUCCESS, "Success")                   \
    X(-1, ERR_ARG, "Invalid argument")         \
    X(-2, ERR_MEM, "Memory allocation failed") \
    X(-3, ERR_LEN, "Invalid message length")   \
    X(-4, ERR_CONFLICT, "Resource conflict")   \
    X(-5, ERR_NO_MSG, "No message available")  \
    X(-6, ERR_FULL, "Queue is full")           \
    X(-7, ERR_EMPTY, "Queue is empty")         \
    X(-8, ERR_TIMEOUT, "Operation timeout")    \
    X(-9, ERR_STOP, "Queue has been stopped")  \
    X(-16, ERR_OTHER, "Other error")

enum
{
#define X(code, name, msg) THREADQUEUE_RET_##name = code,
    THREADQUEUE_FOREACH_ERR(X)
#undef X
};

typedef struct _threadboundedqueue_node_t threadboundedqueue_node_t;
typedef struct _threadunboundedqueue_node_t threadunboundedqueue_node_t;
typedef struct _threadboundedqueue_t threadboundedqueue_t;
typedef struct _threadunboundedqueue_t threadunboundedqueue_t;

/**
 * @brief create a queue
 * @param[in] node_count: the number of queue nodes
 * @param[in] node_size: the size of a node
 * @return threadboundedqueue_t * : success: not threadboundedqueue_t pointer failed: NULL
 */
threadboundedqueue_t *threadboundedqueue_create(size_t node_count, size_t node_size);

/**
 * @brief block push data to a queue
 * @param[in] q: A threadboundedqueue_t pointer
 * @param[in] data : A data pointer to be push queue
 * @param[in] len : data buffer len
 * @param[in] timeout : unit: ms, The wait time if the queue is empty
 * @return int32_t : reference the interface return value defination
 */
int32_t threadboundedqueue_push_block(threadboundedqueue_t *q, void *data, size_t len, uint32_t timeout);

/**
 * @brief nonblock push data to a queue
 * @param[in] q: A threadboundedqueue_t pointer
 * @param[in] data : A data pointer to be push queue
 * @param[in] len : data buffer len
 * @return int32_t : reference the interface return value defination
 */
int32_t threadboundedqueue_push_nonblock(threadboundedqueue_t *q, void *data, size_t len);

/**
 * @brief block pop data from a queue in a blocking way
 * @param[in] q: A threadboundedqueue_t pointer
 * @param[in] data : A data buffer that receives data from a queue
 * @param[in out] len : in: Data buffer max len out: Data buffer real len
 * @param[in] timeout : unit: ms, The wait time if the queue is empty
 * @return int32_t : reference the interface return value defination
 */
int32_t threadboundedqueue_pop_block(threadboundedqueue_t *q, void *data, size_t *len, uint32_t timeout);

/**
 * @brief nonblock pop data from a queue int a none-blocking way
 * @param[in] q: A threadboundedqueue_t pointer
 * @param[in] data : A data buffer that receives data from a queue
 * @param[in out] len : in: Data buffer max len out: Data buffer real len
 * @return int32_t : reference the interface return value defination
 */
int32_t threadboundedqueue_pop_nonblock(threadboundedqueue_t *q, void *data, size_t *len);

/**
 * @brief judge a queue is full
 * @param[in] q: A threadboundedqueue_t pointer
 * @return bool :
 */
bool threadboundedqueue_is_full(threadboundedqueue_t *q);

/**
 * @brief judge a queue is empty
 * @param[in] q: A threadboundedqueue_t pointer
 * @return bool :
 */
bool threadboundedqueue_is_empty(threadboundedqueue_t *q);

/**
 * @brief stop queue
 * @param[in] q: A threadboundedqueue_t pointer
 * @return bool :
 */
bool threadboundedqueue_stop(threadboundedqueue_t *q);

/**
 * @brief uninit queue(stop queue before and ensure any wait queue thread is join)
 * @param[in] q: A threadboundedqueue_t pointer
 * @return void :
 */
void threadboundedqueue_uninit(threadboundedqueue_t **q);

/**
 * @brief create a queue
 * @return threadunboundedqueue_t * : success: not threadunboundedqueue_t pointer failed: NULL
 */
threadunboundedqueue_t *threadunboundedqueue_create(void);

/**
 * @brief push data to a queue
 * @param[in] q: A threadunboundedqueue_t pointer
 * @param[in] data : A data pointer to be push queue
 * @param[in] len : data buffer len
 * @return int32_t : reference the interface return value defination
 */
int32_t threadunboundedqueue_push(threadunboundedqueue_t *q, void *data, size_t len);

/**
 * @brief block pop data from a queue in a blocking way
 * @param[in] q: A threadunboundedqueue_t pointer
 * @param[in] data : A data buffer that receives data from a queue
 * @param[in out] len : in: Data buffer max len out: Data buffer real len
 * @param[in] timeout : unit: ms, The wait time if the queue is empty
 * @return int32_t : reference the interface return value defination
 */
int32_t threadunboundedqueue_pop_block(threadunboundedqueue_t *q, void *data, size_t *len, uint32_t timeout);

/**
 * @brief nonblock pop data from a queue int a none-blocking way
 * @param[in] q: A threadunboundedqueue_t pointer
 * @param[in] data : A data buffer that receives data from a queue
 * @param[in out] len : in: Data buffer max len out: Data buffer real len
 * @return int32_t : reference the interface return value defination
 */
int32_t threadunboundedqueue_pop_nonblock(threadunboundedqueue_t *q, void *data, size_t *len);

/**
 * @brief judge a queue is empty
 * @param[in] q: A threadunboundedqueue_t pointer
 * @return bool :
 */
bool threadunboundedqueue_is_empty(threadunboundedqueue_t *q);

/**
 * @brief stop a quque
 * @param[in] q: A threadunboundedqueue_t pointer
 * @return bool :
 */
bool threadunboundedqueue_stop(threadunboundedqueue_t *q);

/**
 * @brief uninit queue(stop queue before and ensure any wait queue thread is join)
 * @param[in] q: A threadunboundedqueue_t pointer
 * @return void :
 */
void threadunboundedqueue_uninit(threadunboundedqueue_t **q);

/**
 * @brief thread queue error number -> error string
 * @param[in] err: error number
 * @return const char *: error string
 */
const char *threadqueue_strerror(int err);

#ifdef __cplusplus
}
#endif
